#ifndef TABLA_H
#define TABLA_H

#include <map>
#include <string>

using namespace std;

#include "tipo.h"

/******************************************************** 
Enumeración de los tipos de almacenamiento de los valores
asociados a los símbolos:

  -DINAMICO: Variables automáticas de las funciones
  -PARAMETRO: Parametro pasado por la aplicación
  -ESTATICO: Variables globales almacenadas estaticamente
*********************************************************/
enum TipoAlmacenamiento { 
  DINAMICO, 
  PARAMETRO, 
  ESTATICO   
};

/**********************************************************
Enumeración de los tipos de artefacto, nos dice si se trata
de una función o una variable.
**********************************************************/
enum TipoArtefacto {
  VARIABLE,
  FUNCION
};


/******************************************************** 
La estructura Entrada contiene la información que 
almacenamos para cada elemento de la tabla de símbolos:

  art:       	tipo de artefacto (variable o funcion)
  val:       	tipo del símbolo (descrito arriba)
  alm:       	tipo de almacenamiento del valor (descrito 
             	arriba)
  nombre:    	nombre del símbolo
  direccion: 	almacena la dirección donde se almacenará
             	el valor
  longitud:  	almacena el numero de parametros de la funcion
  tipoParametro:almacena el tipo del parametro, en caso de 
		que exista. Si no existe, se deja sin inicializar. 
*********************************************************/
struct Entrada {

public:
  
  TipoArtefacto      art;
  TipoValor          val;
  TipoAlmacenamiento alm;
  std::string nombre, direccion;
  int longitud;
  int numeroDeParametros;
  TipoValor          tipoParametro;
   void nuevoTipoParametro(TipoValor nuevoTipo) {tipoParametro = nuevoTipo;}
  
};

// Campos de este tipo nos garantizan que la memoria se libera bien
// El campo entrada
//typedef std::auto_ptr<Entrada> CampoEntrada;


/******************************************************** 
Declaramos la sobrecarga del operador << para enviar a un 
oputput stream la entrada.
*********************************************************/
std::ostream& operator<<(std::ostream& os,  Entrada& e);

/******************************************************** 
Declaramos la excepcion que se dispara si detectamos que un
elemento esperado no existe en la tabla se símbolos durante una
búsqueda.
*********************************************************/
class ExElementoNoExiste : public std::exception {
  std::string nombre;
 public:
  ExElementoNoExiste(const std::string& pNombre) 
   : nombre(pNombre) {}
  ~ExElementoNoExiste() throw () {}

   const char* what()  throw () {
    return std::string("El elemento " + nombre + " no existe").c_str();
  }
};


/******************************************************** 
Declaramos la excepcion que se dispara si detectamos que
un elemento ya existe al tratar de añadirlo a la tabla 
de símbolos
*********************************************************/
class ExElementoYaExiste : public std::exception {
  std::string nombre;
 public:
  ExElementoYaExiste(const std::string& pNombre)
   : nombre(pNombre) {}
  ~ExElementoYaExiste() throw () {}

   const char* what()  throw () {
    return std::string("El elemento " + nombre + " ya existe").c_str();
  }
};

/******************************************************** 
Declaramos la excepcion que se dispara si detectamos que
un se ha intentado una operación que no corresponde al 
ambito en el que se encuantra el programa.

Acepta un texto como mensaje de la excepción.
*********************************************************/
class ExAmbitoNoValido : public std::exception {
  std::string mensaje;
 public:
  ExAmbitoNoValido(const std::string& pMensaje)
    : mensaje(pMensaje) {}
  ~ExAmbitoNoValido() throw () {}

   const char* what()  throw () {
    return mensaje.c_str();
  }
};


/******************************************************** 
La clase tabla de símbolos representa a nuestra tabla de símbolos
*********************************************************/

class TablaSimbolos
{
 public:

  /******************************************************** 
  Constructor por defecto de la tabla.
  *********************************************************/
  TablaSimbolos();


  /******************************************************** 
  Realiza las operaciones necesarias al comenzar la definición
  de una función.
  Acepta como parámetro el nombre de la función y el tipo de la funcion
  Puede disparar dos tipos de excepciones:
	ExAmbitoNoValido: Si el programador está intentando definir
	una función dentro de otra
	ExElementoYaExiste: Si el programador ha definido un símbolo
	con el mismo nombre.
  *********************************************************/
  void abrirFuncion(const std::string& nombre,Tipo *val)
    throw (ExAmbitoNoValido, ExElementoYaExiste);

  
  
  /******************************************************** 
  Realiza las operaciones necesarias al terminar la definición
  de una función.
 
  Puede disparar una excepción del tipo siguiente:
	ExAmbitoNoValido: Si el programador está cerrando una
	función dentro de otra
  *********************************************************/
  void cerrarFuncion()
    throw (ExAmbitoNoValido);



  /******************************************************** 
  Añade un elemento nuevo a la tabla de símbolos:
  Los parámetros son:
	nombre
	val
	alm
	longitud
	art
  Puede disparar dos tipos de excepciones:
	ExAmbitoNoValido: Falla cuando el elemento
	ExElementoYaExiste: Si el programador ha definido un símbolo
	con el mismo nombre.
  *********************************************************/
  void agregar(const std::string& nombre, Tipo *val, 
	       TipoAlmacenamiento alm = ESTATICO, int longitud = 0, TipoArtefacto art = VARIABLE)
    throw (ExAmbitoNoValido, ExElementoYaExiste);


  void anadeparametro(Tipo& val,  Entrada& entrada);

 /******************************************************** 
  El método buscar devuelve la entrada de la tabla de símbolos
  que corresponde al nombre del parámetro.

  Dispara la excepción ExElementoNoExiste si no encuentra el
  elemento en la tabla.
  *********************************************************/
 Entrada& buscar(const std::string& nombre) 
    throw (ExElementoNoExiste);



 /******************************************************** 
  Este metodo devuelve el número de variables globales en la tabla
  *********************************************************/
  int getNumGlobales() ;

 /******************************************************** 
  Este metodo devuelve el número de variables automáticas de la
  función.
  *********************************************************/
  int getNumLocales()  ;

 /******************************************************** 
  Este metodo devuelve la longitud de la pila de las variables 
  automaticas de la funcion
  *********************************************************/
  int getLongitudLocales() ;

 /******************************************************** 
  Este metodo devuelve el número de parámetros de la función
  *********************************************************/
  int getNumParams() ;

 /******************************************************** 
  Este metodo genera el código necesario para la sección data 
  del programa, y lo llama la función main del analizador 
  sintáctico
  *********************************************************/
  void generar() ;

 /******************************************************** 
  Sobrecarga del operador << sobre output streams para la 
  un objeto del tipo TablaSimbolos, declarado como friend
  para que tenga acceso a los elementos internos del objeto.
  Esta función de sobrecarga se declara más adelante.
  *********************************************************/
  friend std::ostream& operator<<(std::ostream& os,  TablaSimbolos& t);


 private:
 /******************************************************** 
  Definimos un tipo interno TablaInterna, que es un Hash de 
  entradas de tabla de simbolos (tipo Entrada).
  *********************************************************/
  typedef std::map<std::string, Entrada> TablaInterna;

 /******************************************************** 
  Declaramos tres tablas internas: una para almacenar los símbolos globales.
  *********************************************************/
  TablaInterna globales, locales; 

  /******************************************************** 
  Esta variable de estado se utiliza para determinar si el ámbito actual
  se encuentra dentro de una función, o no.
  *********************************************************/
  bool bDentroFuncion;

  
  int  desplParametros;
  int  desplLocales;

  /**********************************************************
  Esta variable se utiliza para determinar la longitud de la pila local 
  de los parametros 
  **********************************************************/
  int  longitudPilaLocales;

};



/******************************************************** 
 Sobrecarga del operador << sobre output streams para la 
 un objeto del tipo TablaSimbolos, declarado como friend
 para que tenga acceso a los elementos internos del objeto
*********************************************************/
std::ostream& operator<<(std::ostream& os,  TablaSimbolos& t);


/******************************************************** 
 Referencia a un objeto externo del tipo TablaSimbolos,
 utilizado por el resto de los programas para referenciar
 la instancia definida en tabla.cpp
*********************************************************/
extern TablaSimbolos tabla;

#endif // TABLA_H
